/**
 * Copyright (c), 上海沃恩信息科技有限公司.
 * All rights reserved.
 * Licensed under BSD (https://www.pittypat.work/bsd.html).
 */

package pittypat;

import pittypat.configuration.PittypatConfig;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;

import pittypat.configuration.AppConfig;
import pittypat.configuration.ConfigException;
import pittypat.configuration.ModuleConfig;
import pittypat.configuration.FunctionConfig;

/**
 * 提供一个功能的元数据信息和配置信息。
 */
public final class FunctionMeta {
	private AppConfig appConfig;
	private ModuleConfig moduleConfig;
	private FunctionConfig config;
	private FunctionAnnotation annotation;
	private ParameterMeta[] parameters;
	private ParameterMeta[] primitiveParameters;
	private ParameterMeta[] complexParameters;
	private String spCommandText;
	
	/**
	 * 在继承类中实现实现目标功能的命令文本。
	 * 
	 * @param meta
	 *            功能的元数据信息。
	 * @return 功能的数据库命令文本。
	 */
	private static String createCommandText(FunctionMeta meta) {
		StringBuilder sb = new StringBuilder(128);
		sb.append("{call ");
		String schema = meta.moduleConfig.getDataSchema();
		if (schema == null || schema.length() == 0) {
			sb.append(String.format("%s_%s.p_%s_%s", meta.appConfig.getDeveloper(), meta.appConfig.getIdentifier(), meta.moduleConfig.getIdentifier(), meta.config.getIdentifier()).toLowerCase());
		} else {
			sb.append(String.format("%s.p_%s_%s", schema, meta.moduleConfig.getIdentifier(), meta.config.getIdentifier()).toLowerCase());
		}
		sb.append("(");

		ParameterMeta[] ps = meta.getPrimitiveParameters();

		if (ps.length > 0) {
			for (int i = 0; i < ps.length; ++i) {
				sb.append("?,");
			}
		}

		// _context, _success, _message
		sb.append("?,?,?)}");

		return sb.toString();
	}

	/**
	 * 使用应用配置、模块配置、功能配置、功能标记属性、参数元数据列表初始化　FunctionMeta　的新实例。
	 * @param appConfig　应用配置信息。
	 * @param moduleConfig　模块配置信息。
	 * @param config　功能配置信息。
	 * @param annotation　功能标记属性。
	 * @param parameters　参数元数据列表。
	 */
	private FunctionMeta(AppConfig appConfig, ModuleConfig moduleConfig, FunctionConfig config,
			FunctionAnnotation annotation, ParameterMeta[] parameters) {
		this.appConfig = appConfig;
		this.moduleConfig = moduleConfig;
		this.config = config;
		this.annotation = annotation;
		this.parameters = parameters;

		if (parameters.length > 0) {
			int pn = 0;
			int cn = 0;
			for (ParameterMeta p : parameters) {
				if (p.isPrimitive()) {
					++pn;
				} else {
					++cn;
				}
			}

			this.primitiveParameters = pn == 0 ? EMPTY_PARAMETERS : new ParameterMeta[pn];
			this.complexParameters = cn == 0 ? EMPTY_PARAMETERS : new ParameterMeta[cn];

			pn = 0;
			cn = 0;
			for (ParameterMeta p : parameters) {
				if (p.isPrimitive()) {
					this.primitiveParameters[pn++] = p;
				} else {
					this.complexParameters[cn++] = p;
				}
			}
		} else {
			this.primitiveParameters = EMPTY_PARAMETERS;
			this.complexParameters = EMPTY_PARAMETERS;
		}
		
		// 存储过程命令文本
		this.spCommandText = createCommandText(this);
	}

	/**
	 * 获取目标 App 的配置信息。
	 * 
	 * @return App 的配置信息。
	 */
	public AppConfig getAppConfig() {
		return this.appConfig;
	}

	/**
	 * 获取目标模块的配置信息。
	 * 
	 * @return 模块的配置信息。
	 */
	public ModuleConfig getModuleConfig() {
		return this.moduleConfig;
	}

	/**
	 * 获取目标功能的配置信息。
	 * 
	 * @return 功能的配置信息。
	 */
	public FunctionConfig getConfig() {
		return this.config;
	}
	
	/**
	 * 获取功能对应的存储过程命令文本。
	 * @return 存储过程命令文本。
	 */
	public String getSpCommandText() {
		return this.spCommandText;
	}

	/**
	 * 获取目标功能的标记信息。
	 * 
	 * @return 功能的标记信息。
	 */
	public FunctionAnnotation getAnnotation() {
		return this.annotation;
	}

	/**
	 * 获取目标功能上所有参数的元数据信息。
	 * 
	 * @return 功能的所有参数的元数据信息。
	 */
	public ParameterMeta[] getParameters() {
		return this.parameters;
	}

	/**
	 * 获取目标功能上所有基础类型参数的元数据信息。
	 * 
	 * @return 功能的所有基础类型参数的元数据信息。
	 */
	public ParameterMeta[] getPrimitiveParameters() {
		return this.primitiveParameters;
	}

	/**
	 * 获取目标功能上所有复杂类型参数的元数据信息。
	 * 
	 * @return 功能的所有复杂类型参数的元数据信息。
	 */
	public ParameterMeta[] getComplexParameters() {
		return this.complexParameters;
	}

	// 元数据缓存字典
	private static final HashMap<Class<?>, FunctionMeta> cachedMetas = new HashMap<Class<?>, FunctionMeta>();
	// 同步对缓存字典的访问
	private static final Object CACHED_METAS_LOCK = new Object();
	// 表示一个空的参数列表
	private static final ParameterMeta[] EMPTY_PARAMETERS = new ParameterMeta[0];
	// 表示一个默认的 FunctionAnnotation，当没有在 FunctionArgs 类型上声明 FunctionAnnotation
	// 时使用此默认的 FunctionAnnotation
	private static final FunctionAnnotation DEFAULT_FUNCTION_ANNOTATION = new FunctionAnnotation() {
		@Override
		public boolean transaction() {
			return false;
		}

		@Override
		public Class<? extends Annotation> annotationType() {
			return FunctionAnnotation.class;
		}
	};

	/**
	 * 获取指定 FunctionArgs 类型对应的功能配置信息和参数元数据列表。该方法会在内部缓存此前加载过的元数据，以便减少反射带来的性能损失。
	 * 
	 * @param argsType
	 *            从 FunctionArgs 继承的类型。
	 * @return 目标类型对应功能的配置信息和参数信息。
	 * @throws ConfigException
	 */
	public static FunctionMeta find(Class<?> argsType) throws ConfigException {
		if (argsType == null) {
			throw new IllegalArgumentException("argsType 为 null。");
		}

		if (!FunctionArgs.class.isAssignableFrom(argsType)) {
			throw new IllegalArgumentException(argsType.getName() + " 不是一个 FunctionArgs 类型。");
		}

		synchronized (CACHED_METAS_LOCK) {
			if (cachedMetas.containsKey(argsType)) {
				return cachedMetas.get(argsType);
			}

			// 包完整名称，期待的格式为：com.{developerCode}.{appIdentifier}.{moduleIdentifier}.args
			String packName = argsType.getPackage().getName();
			String[] names = packName.split("\\.");

			if (names.length < 4) {
				throw new IllegalArgumentException("无法从 " + argsType.getName() + " 的包名称中解析开发者代码和 App 标识符。");
			}

			AppConfig appConfig = PittypatConfig.getCurrent().getAppConfig(names[1], names[2]);
			if (appConfig == null) {
				throw new ConfigException(String.format("没有找到 App '%s.%s' 的配置信息。", names[1], names[2]));
			}

			ModuleConfig moduleConfig = appConfig.getModuleConfig(names[3]);
			if (moduleConfig == null) {
				throw new ConfigException(String.format("没有找到模块 '%s.%s.%s' 的配置信息。", names[1], names[2], names[3]));
			}

			FunctionConfig funcConfig = moduleConfig.getFunctionConfig(argsType.getSimpleName());
			if (funcConfig == null) {
				throw new ConfigException(String.format("没有找到功能 '%s' 的配置信息。", argsType.getName()));
			}

			FunctionAnnotation annotation = argsType.getDeclaredAnnotation(FunctionAnnotation.class);
			if (annotation == null) {
				annotation = DEFAULT_FUNCTION_ANNOTATION;
			}

			// 参数列表
			ParameterMeta[] parameters = null;
			Field[] fields = argsType.getDeclaredFields();
			if (fields.length > 0) {
				ArrayList<ParameterMeta> list = new ArrayList<ParameterMeta>(fields.length);
				ParameterAnnotation pa = null;

				for (Field f : fields) {
					pa = f.getDeclaredAnnotation(ParameterAnnotation.class);
					if (pa != null) {
						list.add(new ParameterMeta(pa, f));
					}
				}

				if (list.size() > 0) {
					parameters = new ParameterMeta[list.size()];
					list.toArray(parameters);
				}
			}

			FunctionMeta meta = new FunctionMeta(appConfig, moduleConfig, funcConfig, annotation,
					parameters == null ? EMPTY_PARAMETERS : parameters);
			cachedMetas.put(argsType, meta);

			return meta;
		}
	}
}
